#include "GameApp.h"
#include "Sensor.h"

//-------------------------------------------------------------------------------------
BaseObject::BaseObject(std::string objectID)
	: VisibleObject(objectID), mVelAngle(0.0f), mIdleSound(0), 
	hasSensors(false), mpNode(0)
{
	mpBulletInitData = BulletDataPtr(new BulletData());

	boost::shared_ptr<SettingsManager> sm(new SettingsManager(objectID));
	readBaseObjectFromXML(sm);
	mTypes.push_front(OBJECT_BASE);
	mpBody = 0;
}
//-------------------------------------------------------------------------------------
BaseObject::BaseObject(std::string objectID, std::string name, btVector3 &pos, btQuaternion &rot, btVector3 &scale, std::string mesh)
	: VisibleObject(objectID, name, pos, rot, scale, mesh), mVelAngle(0.0f), mIdleSound(0), 
	hasSensors(false), mpNode(0)
{
	mpBulletInitData = BulletDataPtr(new BulletData());

	boost::shared_ptr<SettingsManager> sm(new SettingsManager(objectID));
	readBaseObjectFromXML(sm);
	mTypes.push_front(OBJECT_BASE);
	mpBody = 0;
}
//-------------------------------------------------------------------------------------
BaseObject::BaseObject(boost::shared_ptr<SettingsManager> sm)
	: VisibleObject(sm), mVelAngle(0.0f), mIdleSound(0), 
	hasSensors(false), mpNode(0)
{
	mpBulletInitData = BulletDataPtr(new BulletData());
	readBaseObjectFromXML(sm);
	mTypes.push_front(OBJECT_BASE);
	mpBody = 0;
}
//-------------------------------------------------------------------------------------
BaseObject::BaseObject(const OgreDataPtr ogreData, const BulletDataPtr bulletData)
	: VisibleObject(ogreData), mpBulletInitData(bulletData), mVelAngle(0.0f), mIdleSound(0), 
	hasSensors(false), mpNode(0)
{
	mTypes.push_front(OBJECT_BASE);
	mpBody = 0;
}
//-------------------------------------------------------------------------------------
BaseObject::BaseObject(boost::shared_ptr<BaseObject> ref)
	: VisibleObject(ref), mpNode(0)
{
	mpBulletInitData = ref->getBulletInitData();
	mVelAngle = ref->getVelAngle();
	mIdleSound = 0;
	hasSensors = false;
	
	mTypes.push_front(OBJECT_BASE);
	mpBody = 0;

	// TODO: copy sensors
}
//-------------------------------------------------------------------------------------
BaseObject::~BaseObject(void)
{
}
//-------------------------------------------------------------------------------------
void BaseObject::readBaseObjectFromXML(boost::shared_ptr<SettingsManager> sm)
{
	mpBulletInitData->collisionName = OStringPtr(new Ogre::String(sm->getSetting("BulletData/Name/object").c_str()));
	mpBulletInitData->collisionMeshName = OStringPtr(new Ogre::String(sm->getSetting("BulletData/Name/mesh").c_str()));
	//Properties
	mpBulletInitData->mass = btScalar(atof(sm->getSetting("BulletData/Properties/mass").c_str()));
	mpBulletInitData->shape = (ShapeType) atoi(sm->getSetting("BulletData/Properties/shape").c_str());
	mpBulletInitData->extraNode = atoi(sm->getSetting("BulletData/Properties/node").c_str()) == 1;
	mpBulletInitData->restitution = btScalar(atof(sm->getSetting("BulletData/Properties/restitution").c_str()));
	mpBulletInitData->friction = btScalar(atof(sm->getSetting("BulletData/Properties/friction").c_str()));
	//Damping
	mpBulletInitData->angDamp = btScalar(atof(sm->getSetting("BulletData/Damping/angular").c_str()));
	mpBulletInitData->linDamp = btScalar(atof(sm->getSetting("BulletData/Damping/linear").c_str()));
	//AngularFactor
	mpBulletInitData->angularFactX = btScalar(atof(sm->getSetting("BulletData/AngularFactor/x").c_str()));
	mpBulletInitData->angularFactY = btScalar(atof(sm->getSetting("BulletData/AngularFactor/y").c_str()));
	mpBulletInitData->angularFactZ = btScalar(atof(sm->getSetting("BulletData/AngularFactor/z").c_str()));
	//Collision
	mpBulletInitData->type = (short) atoi(sm->getSetting("BulletData/Collision/type").c_str());
	mpBulletInitData->mask = (short) atoi(sm->getSetting("BulletData/Collision/mask").c_str());
	mpBulletInitData->isGhost = atoi(sm->getSetting("BulletData/Collision/isGhost").c_str()) == 1;
}
//-------------------------------------------------------------------------------------
const BulletDataPtr BaseObject::getBulletInitData(void) const
{
	return mpBulletInitData;
}
//-------------------------------------------------------------------------------------
void BaseObject::setBody(btRigidBody *body)
{
	mpBody = body;
}
//-------------------------------------------------------------------------------------
btRigidBody *BaseObject::getBody(void) const
{
	return mpBody;
}
//-------------------------------------------------------------------------------------
void BaseObject::setNode(Ogre::SceneNode *node)
{
	mpNode = node;
}
//-------------------------------------------------------------------------------------
Ogre::SceneNode *BaseObject::getNode(void) const
{
	return mpNode;
}
//-------------------------------------------------------------------------------------
btScalar BaseObject::getVelAngle(void)const
{
	return  mVelAngle;
}
//-------------------------------------------------------------------------------------
void BaseObject::setVelAngle(btScalar angle)
{
	mVelAngle = angle;
}
//-------------------------------------------------------------------------------------
void BaseObject::setSound(const SoundType type, const ALuint source)
{
	if (type == SOUND_IDLE) mIdleSound = source;
}
//-------------------------------------------------------------------------------------
const ALuint BaseObject::getSound(const SoundType type) const
{
	if (type == SOUND_IDLE) return mIdleSound;

	return 0;
}
//-------------------------------------------------------------------------------------
bool BaseObject::dead(void) const
{
	return mbDead;
}
//-------------------------------------------------------------------------------------
void BaseObject::kill(void)
{
	if (mpBody) gpApp->mpGameLogic->getPhysics()->removeBody(mpBody);
	mpBody = 0;
	
	if(!mbDead) mpAnimator->resetAnimations();
	if (mpNode)
	{
		gpApp->mpView->removeObject(mpNode);
		mpNode = 0;
		mpEntityNode = 0;
	}

	//remove sensors if created
	if(hasSensors)
	{
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(left);
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(right);
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(top);
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(bottom);
		left->kill();
		right->kill();
		top->kill();
		bottom->kill();
	}

	mbDead = true;
}
//-------------------------------------------------------------------------------------
void BaseObject::collision(boost::shared_ptr<BaseObject> obj, btVector3 &point)
{
}
//-------------------------------------------------------------------------------------
void BaseObject::update(const unsigned long elapsedTime)
{
	mpAnimator->stepAnimation(elapsedTime);
}
//-------------------------------------------------------------------------------------
void BaseObject::addSensors(void)
{
	strstream s1, s2, s3, s4;
	s1 << "trigger" << gpApp->mpGameLogic->nameCounter++;
	s2 << "trigger" << gpApp->mpGameLogic->nameCounter++;
	s3 << "trigger" << gpApp->mpGameLogic->nameCounter++;
	s4 << "trigger" << gpApp->mpGameLogic->nameCounter++;
	btVector3 min, max, abs, size, thickness, margin;
	btTransform id = btTransform::getIdentity();
	getBody()->getCollisionShape()->getAabb(id, min, max);
	abs = max - min;
	thickness = btVector3(0.1f,0.3f,0.f);
	margin = btVector3(0.5f, 0.6f,0.f);
	size = abs - margin;

	left = SensorPtr(new Sensor(shared_from_this(), SENSOR_LEFT));
	left->getOgreInitData()->scale = Ogre::Vector3(thickness.x(), size.y(), size.z());
	left->getOgreInitData()->name = OStringPtr(new Ogre::String(s1.str()));
	gpApp->mpGameLogic->addObject(left);

	right = SensorPtr(new Sensor(shared_from_this(), SENSOR_RIGHT));
	right->getOgreInitData()->scale = Ogre::Vector3(thickness.x(), size.y(), size.z());
	right->getOgreInitData()->name = OStringPtr(new Ogre::String(s2.str()));
	gpApp->mpGameLogic->addObject(right);

	top = SensorPtr(new Sensor(shared_from_this(), SENSOR_TOP));
	top->getOgreInitData()->scale = Ogre::Vector3(size.x(), thickness.y(), size.z());
	top->getOgreInitData()->name = OStringPtr(new Ogre::String(s3.str()));
	gpApp->mpGameLogic->addObject(top);

	bottom = SensorPtr(new Sensor(shared_from_this(), SENSOR_BOTTOM));
	bottom->getOgreInitData()->scale = Ogre::Vector3(size.x(), thickness.y(), size.z());
	bottom->getOgreInitData()->name = OStringPtr(new Ogre::String(s4.str()));
	gpApp->mpGameLogic->addObject(bottom);
	
	id.setOrigin(btVector3(min.x() + 0.5f * thickness.x(),0.f,0.f));
	gpApp->mpGameLogic->getUpdatePairs()->attachObjectToObject(left, shared_from_this(), id);
	id.setOrigin(btVector3(max.x() - 0.5f * thickness.x(),0.f,0.f));
	gpApp->mpGameLogic->getUpdatePairs()->attachObjectToObject(right, shared_from_this(), id);
	id.setOrigin(btVector3(0.f,max.y() - 0.5f * thickness.y(),0.f));
	gpApp->mpGameLogic->getUpdatePairs()->attachObjectToObject(top, shared_from_this(), id);
	id.setOrigin(btVector3(0.f,min.y() + 0.5f * thickness.y(),0.f));
	gpApp->mpGameLogic->getUpdatePairs()->attachObjectToObject(bottom, shared_from_this(), id);

	hasSensors = true;
}
//-------------------------------------------------------------------------------------
void BaseObject::sensorCollision(boost::shared_ptr<BaseObject> obj, SensorPosition pos)
{
	switch(pos)
	{
	case SENSOR_LEFT:
		break;
	case SENSOR_RIGHT:
		break;
	case SENSOR_TOP:
		break;
	default:
		break;
	}
}
//-------------------------------------------------------------------------------------
btVector3 BaseObject::getLocalVelocity(void)
{
	btMatrix3x3 &rot = mpBody->getWorldTransform().getBasis();
	return rot.inverse() * mpBody->getLinearVelocity();
}
//-------------------------------------------------------------------------------------
btVector3 BaseObject::getLocalVector(const btVector3 &vec)
{
	return mpBody->getWorldTransform().inverse() * vec;
}
//-------------------------------------------------------------------------------------
btVector3 BaseObject::getGlobalVector(const btVector3 &vec)
{
	return mpBody->getWorldTransform() * vec;
}
//-------------------------------------------------------------------------------------
void BaseObject::setLocalVelocity(btVector3 &vel)
{
	btMatrix3x3 &rot = mpBody->getWorldTransform().getBasis();
	mpBody->setLinearVelocity(rot * vel);
}
//-------------------------------------------------------------------------------------
btVector3 BaseObject::physicsSize(void)
{
	btVector3 min, max;
	btTransform id = btTransform::getIdentity();
	getBody()->getCollisionShape()->getAabb(id, min, max);
	return max - min;
}
//-------------------------------------------------------------------------------------
JointPtr BaseObject::getJoint(void)
{
	return mpJoint;
}
//-------------------------------------------------------------------------------------
void BaseObject::setJoint(JointPtr joint)
{
	mpJoint = joint;
}
//-------------------------------------------------------------------------------------
void BaseObject::scalePower(const char power)
{
}